using System;
using System.Net;
using System.Net.Sockets;
using System.Collections;

namespace LANamp
{
	/// <summary>
	/// Contains a list of known LANamp clients
	/// </summary>
	public class ClientList
	{
		private ArrayList clients;
		private int lives;
        private Client nextClient;
		public delegate void UpdateEvent(string IPaddress);
		public event UpdateEvent Updated;
	
		public ClientList(int Lives)
		{
			clients = new ArrayList();
			lives = Lives;
            nextClient = null;
		}
		
		public ClientList(): this(5)
		{
		}
		
		public Client GetClient(String IPaddress)
		{
			Client gc = null;
            lock (clients)
            {
                foreach (Client client in clients)
                {
                    if (client.IPaddress == IPaddress) gc = client;
                }
            }
			return gc;
		}

		public Client GetClient(EndPoint ep)
		{
			return GetClient(Client.ToIP(ep));
		}

        public Client NewConnection(Socket newSocket)
        {
            string IPaddress = Client.ToIP(newSocket.RemoteEndPoint);
            Client gc = null;
            lock (clients)
            {
                foreach (Client client in clients)
                {
                    if (client.IPaddress == IPaddress)
                    {
                        client.ServerSocket = newSocket;
                        gc = client;
                    }
                }
            }
            return gc;
        }

        public void Alive(String IPaddress, String Server, String Nickname, String Hostname, String AppVersion)
		{
			bool handled = false;
            ArrayList updatedservers = new ArrayList();
            lock (clients)
            {
                foreach (Client client in clients)
                {
                    if (client.IPaddress == IPaddress)
                    {
                        handled = true;
                        client.Lives = Lives;
                        if (client.Server != Server)
                        {
                            String oldserver = client.Server;
                            client.Server = Server;
                            updatedservers.Add(oldserver);
                            updatedservers.Add(Server);
                        }
                        else
                        {
                            if (client.Nickname != Nickname)
                            {
                                client.Nickname = Nickname;
                                updatedservers.Add(Server);
                            }
                            if (client.Hostname != Hostname)
                            {
                                client.Hostname = Hostname;
                                updatedservers.Add(Server);
                            }
                        }
                    }
                }
            }
			if (! handled) 
			{
                lock (clients)
                {
                    clients.Add(new Client(IPaddress, Server, Nickname, Hostname, AppVersion, Lives));
                    if (clients.Count == 1) nextClient = (Client)clients[0];
                }
                updatedservers.Add(Server);
			}
            if (Updated != null)
            {
                foreach (string server in updatedservers)
                {
                    Updated(server);
                }
            }
		}
		
		public void Alive(EndPoint ep, String Server, String Nickname, String Hostname, String AppVersion)
		{
			Alive(Client.ToIP(ep), Server, Nickname, Hostname, AppVersion);
		}
		
		public void Gone(String IPaddress)
		{
            Client goneClient = null;
            lock (clients)
            {
                foreach (Client client in clients)
                {
                    if (client.IPaddress == IPaddress)
                    {
                        goneClient = client;
                        break;
                    }
                }
            }
            if (goneClient != null)
            {
                string server = goneClient.Server;
                lock(clients) {
                    if (goneClient == nextClient)
                    {
                        if (clients.Count == 1)
                            // last remaining client
                            nextClient = null;
                        else
                            if (clients[clients.Count - 1] == nextClient)
                                // last client in list
                                nextClient = (Client)clients[0];
                            else
                                // just select next client
                                nextClient = (Client)clients[clients.IndexOf(nextClient)+1];
                    }
                    clients.Remove(goneClient);
                }
                if (Updated != null) Updated(server);
            }
        }

		public void Gone(EndPoint ep)
		{
			Gone(Client.ToIP(ep));
		}

		public ArrayList Items
		{
			get
			{
                lock (clients)
                {
                    return clients;
                }
			}
		}
        
        public string NextClientInfo
        {
            get
            {
                lock (clients)
                {
                    if (nextClient != null)
                        return nextClient.Nickname + "@" + nextClient.Hostname.ToLower();
                    else
                        return "(none)";
                }
            }
        }

        public Client NextClient
        {
            get
            {
                lock (clients)
                {
                    return nextClient;
                }
            }
        }

        public Client CycleClient()
        {
            lock (clients)
            {
                Client currentNext = nextClient;
                if (clients.Count > 1)
                {
                    if (clients[clients.Count - 1] == currentNext)
                        // last client in list
                        nextClient = (Client)clients[0];
                    else
                        // just select next client
                        nextClient = (Client)clients[clients.IndexOf(currentNext) + 1];
                }
                return currentNext;
            }
        }

        public int Count
		{
			get
			{
                lock (clients)
                {
                    return clients.Count;
                }
			}
		}

        public int CountOnServer(Server server)
        {
            int i = 0;
            lock (clients)
            {
                foreach (Client client in clients)
                {
                    if (client.Server == server.IPaddress) i++;
                }
            }
            return i;
        }

        public int SkipVoteCount
        {
            get
            {
                lock (clients)
                {
                    int i = 0;
                    foreach (Client client in clients)
                    {
                        if (client.SkipVote) i++;
                    }
                    return i;
                }
            }
        }

        public void ClearSkipVotes()
        {
            lock (clients)
            {
                foreach (Client client in clients)
                {
                    client.ClearSkipVote();
                }
            }
        }

		public void TimeOut()
		{
            ArrayList updatedservers = new ArrayList();
            lock (clients)
            {
                for (int i = clients.Count; i > 0; i--)
                {
                    Client client = clients[i - 1] as Client;
                    client.Lives -= 1;
                    if (client.Lives <= 0)
                    {
                        String server = client.Server;
                        if (client == nextClient)
                        {
                            if (clients.Count == 1)
                                // last remaining client
                                nextClient = null;
                            else
                                if (clients[clients.Count - 1] == nextClient)
                                    // last client in list
                                    nextClient = (Client)clients[0];
                                else
                                    // just select next client
                                    nextClient = (Client)clients[clients.IndexOf(nextClient)+1];
                        }
                        clients.Remove(client);
                        updatedservers.Add(server);
                    }
                }
            }
            foreach (string server in updatedservers)
            {
                if (Updated != null) Updated(server);
            }
            updatedservers.Clear();
        }
		
		public int Lives
		{
			get
			{
				return lives;
			}
			set
			{
				lives = value;
			}
		}
	}
}
